import sys
import os
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QLabel, QPushButton, QFileDialog, QSlider, QSpinBox, QComboBox,
                             QTabWidget, QMessageBox, QSplitter, QGroupBox, QGridLayout,
                             QAction, QToolBar, QStatusBar, QInputDialog, QColorDialog)
from PyQt5.QtGui import QPixmap, QImage, QPainter, QPen, QColor, QCursor, QFont
from PyQt5.QtCore import Qt, QPoint, QRect, QTimer, pyqtSignal
import cv2
from PIL import Image, ImageQt


class ImageProcessor:
    """图像处理核心类，实现各种图像处理算法"""

    def __init__(self):
        self.original_image = None  # 原始图像
        self.current_image = None  # 当前处理后的图像
        self.temp_image = None  # 临时图像（用于预览）
        self.file_path = None  # 当前图像文件路径
        self.modified = False  # 图像是否已修改

    def load_image(self, file_path):
        """加载图像"""
        try:
            self.file_path = file_path
            self.original_image = cv2.imread(file_path)
            if self.original_image is None:
                # 尝试使用PIL加载
                pil_img = Image.open(file_path)
                self.original_image = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)

            self.current_image = self.original_image.copy()
            self.modified = False
            return True
        except Exception as e:
            print(f"加载图像失败: {e}")
            return False

    def rotate(self, angle):
        """旋转图像"""
        if self.current_image is None:
            return False

        (h, w) = self.current_image.shape[:2]
        center = (w // 2, h // 2)
        M = cv2.getRotationMatrix2D(center, angle, 1.0)
        self.current_image = cv2.warpAffine(self.current_image, M, (w, h))
        self.modified = True
        return True

    def resize(self, scale_percent):
        """缩放图像"""
        if self.current_image is None:
            return False

        width = int(self.current_image.shape[1] * scale_percent / 100)
        height = int(self.current_image.shape[0] * scale_percent / 100)
        self.current_image = cv2.resize(self.current_image, (width, height), interpolation=cv2.INTER_AREA)
        self.modified = True
        return True

    def crop(self, x, y, width, height):
        """裁剪图像"""
        if self.current_image is None:
            return False

        self.current_image = self.current_image[y:y + height, x:x + width]
        self.modified = True
        return True

    def adjust_brightness_contrast(self, brightness=0, contrast=0):
        """调整亮度和对比度"""
        if self.current_image is None:
            return False

        # 确保对比度在合理范围内
        contrast = max(-100, min(100, contrast))
        brightness = max(-100, min(100, brightness))

        # 转换对比度为alpha值 (0.5-2.0)
        alpha = 1.0 + contrast / 100.0
        beta = brightness

        # 应用亮度和对比度调整
        self.current_image = cv2.convertScaleAbs(self.current_image, alpha=alpha, beta=beta)
        self.modified = True
        return True

    def adjust_saturation(self, value):
        """调整饱和度"""
        if self.current_image is None:
            return False

        # 转换为HSV色彩空间
        hsv = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2HSV)
        h, s, v = cv2.split(hsv)

        # 调整饱和度
        s = np.clip(s * (1 + value / 100.0), 0, 255).astype(hsv.dtype)

        # 合并回HSV并转换回BGR
        hsv = cv2.merge([h, s, v])
        self.current_image = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
        self.modified = True
        return True

    def smooth(self, method, kernel_size=3):
        """平滑处理"""
        if self.current_image is None:
            return False

        if method == "均值滤波":
            self.current_image = cv2.blur(self.current_image, (kernel_size, kernel_size))
        elif method == "高斯滤波":
            self.current_image = cv2.GaussianBlur(self.current_image, (kernel_size, kernel_size), 0)
        elif method == "中值滤波":
            self.current_image = cv2.medianBlur(self.current_image, kernel_size)
        elif method == "双边滤波":
            self.current_image = cv2.bilateralFilter(self.current_image, kernel_size, 75, 75)

        self.modified = True
        return True

    def detect_shapes(self):
        """形状识别"""
        if self.current_image is None:
            return False

        # 创建副本以保留原始图像
        output = self.current_image.copy()
        gray = cv2.cvtColor(self.current_image, cv2.COLOR_BGR2GRAY)
        blurred = cv2.GaussianBlur(gray, (5, 5), 0)
        thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY)[1]

        # 查找轮廓
        contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # 检测形状
        for c in contours:
            # 计算轮廓周长
            peri = cv2.arcLength(c, True)
            # 执行轮廓近似
            approx = cv2.approxPolyDP(c, 0.04 * peri, True)

            # 根据顶点数量确定形状
            if len(approx) == 3:
                shape = "三角形"
            elif len(approx) == 4:
                # 计算轮廓的边界框
                (x, y, w, h) = cv2.boundingRect(approx)
                ar = w / float(h)
                # 根据宽高比确定是正方形还是矩形
                shape = "正方形" if ar >= 0.95 and ar <= 1.05 else "矩形"
            elif len(approx) > 4:
                # 计算轮廓面积与边界圆面积的比例
                area = cv2.contourArea(c)
                ((x, y), radius) = cv2.minEnclosingCircle(c)
                circle_area = np.pi * radius * radius
                ratio = area / circle_area

                if ratio > 0.8:
                    shape = "圆形"
                else:
                    shape = "多边形"

            # 在图像上绘制轮廓和形状名称
            cv2.drawContours(output, [c], -1, (0, 255, 0), 2)
            cv2.putText(output, shape, (approx[0][0][0], approx[0][0][1] - 10),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

        self.current_image = output
        self.modified = True
        return True

    def stitch_images(self, images):
        """图像拼接"""
        if not images:
            return False

        # 创建拼接器
        stitcher = cv2.Stitcher_create() if cv2.__version__.startswith('4') else cv2.createStitcher()

        # 执行拼接
        status, result = stitcher.stitch(images)

        if status == cv2.Stitcher_OK:
            self.current_image = result
            self.modified = True
            return True
        else:
            print(f"拼接失败，状态码: {status}")
            return False

    def to_qimage(self, image):
        """将OpenCV图像转换为QImage"""
        if image is None:
            return QImage()

        if len(image.shape) == 3:  # BGR image
            h, w, c = image.shape
            return QImage(image.data, w, h, w * c, QImage.Format_BGR888)
        else:  # Grayscale image
            h, w = image.shape
            return QImage(image.data, w, h, w, QImage.Format_Grayscale8)


class ClickableImageLabel(QLabel):
    """可点击的图像标签，用于显示图像并支持裁剪区域选择"""

    region_selected = pyqtSignal(int, int, int, int)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setMinimumSize(1, 1)  # 允许标签调整为任意大小
        self.setAlignment(Qt.AlignCenter)
        self.drawing = False
        self.start_point = QPoint()
        self.end_point = QPoint()
        self.current_pixmap = None

    def setPixmap(self, pixmap):
        """设置图像"""
        self.current_pixmap = pixmap
        super().setPixmap(pixmap.scaled(
            self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

    def resizeEvent(self, event):
        """调整大小时重绘图像"""
        if self.current_pixmap:
            super().setPixmap(self.current_pixmap.scaled(
                self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
        super().resizeEvent(event)

    def mousePressEvent(self, event):
        """鼠标按下事件"""
        if event.button() == Qt.LeftButton:
            self.drawing = True
            self.start_point = event.pos()
            self.end_point = event.pos()
            self.update()

    def mouseMoveEvent(self, event):
        """鼠标移动事件"""
        if self.drawing:
            self.end_point = event.pos()
            self.update()

    def mouseReleaseEvent(self, event):
        """鼠标释放事件"""
        if event.button() == Qt.LeftButton and self.drawing:
            self.drawing = False
            self.end_point = event.pos()

            # 计算选择区域
            x = min(self.start_point.x(), self.end_point.x())
            y = min(self.start_point.y(), self.end_point.y())
            w = abs(self.start_point.x() - self.end_point.x())
            h = abs(self.start_point.y() - self.end_point.y())

            # 如果选择了有效区域，则发出信号
            if w > 0 and h > 0:
                # 将坐标转换为图像中的实际坐标
                if self.current_pixmap:
                    pixmap_size = self.current_pixmap.size()
                    label_size = self.size()

                    # 计算缩放比例
                    scale_x = pixmap_size.width() / label_size.width()
                    scale_y = pixmap_size.height() / label_size.height()

                    # 考虑保持纵横比时的居中对齐
                    if label_size.width() * pixmap_size.height() > label_size.height() * pixmap_size.width():
                        # 图像在垂直方向上有黑边
                        offset_y = (label_size.height() - pixmap_size.height() / scale_x) / 2
                        y = max(0, y - offset_y)
                        scale_y = scale_x
                    else:
                        # 图像在水平方向上有黑边
                        offset_x = (label_size.width() - pixmap_size.width() / scale_y) / 2
                        x = max(0, x - offset_x)
                        scale_x = scale_y

                    # 发送实际图像坐标
                    self.region_selected.emit(
                        int(x * scale_x), int(y * scale_y),
                        int(w * scale_x), int(h * scale_y)
                    )

            self.update()

    def paintEvent(self, event):
        """绘制事件"""
        super().paintEvent(event)

        # 如果正在绘制选择框，则绘制它
        if self.drawing and not self.start_point.isNull() and not self.end_point.isNull():
            painter = QPainter(self)
            painter.setPen(QPen(Qt.red, 2, Qt.DashLine))
            rect = QRect(self.start_point, self.end_point)
            painter.drawRect(rect)


class MainWindow(QMainWindow):
    """主窗口类"""

    def __init__(self):
        super().__init__()
        self.processor = ImageProcessor()
        self.init_ui()
        self.images_to_stitch = []  # 存储待拼接的图像

    def init_ui(self):
        """初始化用户界面"""
        # 设置窗口标题和大小
        self.setWindowTitle("计算机视觉开发平台")
        self.setGeometry(100, 100, 1200, 800)

        # 创建菜单栏
        self.create_menu_bar()

        # 创建工具栏
        self.create_tool_bar()

        # 创建中心部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        # 创建主布局
        main_layout = QHBoxLayout(central_widget)

        # 创建左侧控制面板
        control_panel = self.create_control_panel()

        # 创建右侧显示区域
        display_area = self.create_display_area()

        # 添加到主布局
        main_layout.addWidget(control_panel, 1)  # 1/4宽度
        main_layout.addWidget(display_area, 3)  # 3/4宽度

        # 创建状态栏
        self.statusBar().showMessage("就绪")

        # 显示窗口
        self.show()

    def create_menu_bar(self):
        """创建菜单栏"""
        menubar = self.menuBar()

        # 文件菜单
        file_menu = menubar.addMenu("文件")

        # 打开动作
        open_action = QAction("打开", self)
        open_action.setShortcut("Ctrl+O")
        open_action.triggered.connect(self.open_image)
        file_menu.addAction(open_action)

        # 保存动作
        save_action = QAction("保存", self)
        save_action.setShortcut("Ctrl+S")
        save_action.triggered.connect(self.save_image)
        file_menu.addAction(save_action)

        # 保存为动作
        save_as_action = QAction("保存为", self)
        save_as_action.setShortcut("Ctrl+Shift+S")
        save_as_action.triggered.connect(self.save_image_as)
        file_menu.addAction(save_as_action)

        file_menu.addSeparator()

        # 退出动作
        exit_action = QAction("退出", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # 编辑菜单
        edit_menu = menubar.addMenu("编辑")

        # 撤销动作
        undo_action = QAction("撤销", self)
        undo_action.setShortcut("Ctrl+Z")
        undo_action.triggered.connect(self.undo)
        edit_menu.addAction(undo_action)

        # 重做动作
        redo_action = QAction("重做", self)
        redo_action.setShortcut("Ctrl+Y")
        edit_menu.addAction(redo_action)

        # 编辑菜单分隔符
        edit_menu.addSeparator()

        # 旋转动作
        rotate_action = QAction("旋转", self)
        rotate_action.triggered.connect(self.rotate_image)
        edit_menu.addAction(rotate_action)

        # 缩放动作
        resize_action = QAction("缩放", self)
        resize_action.triggered.connect(self.resize_image)
        edit_menu.addAction(resize_action)

        # 裁剪动作
        crop_action = QAction("裁剪", self)
        crop_action.triggered.connect(self.enable_crop_mode)
        edit_menu.addAction(crop_action)

        # 帮助菜单
        help_menu = menubar.addMenu("帮助")

        # 关于动作
        about_action = QAction("关于", self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)

    def create_tool_bar(self):
        """创建工具栏"""
        toolbar = QToolBar("工具栏")
        self.addToolBar(toolbar)

        # 打开按钮
        open_btn = QAction("打开", self)
        open_btn.setIcon(self.style().standardIcon(self.style().SP_DialogOpenButton))
        open_btn.triggered.connect(self.open_image)
        toolbar.addAction(open_btn)

        # 保存按钮
        save_btn = QAction("保存", self)
        save_btn.setIcon(self.style().standardIcon(self.style().SP_DialogSaveButton))
        save_btn.triggered.connect(self.save_image)
        toolbar.addAction(save_btn)

        toolbar.addSeparator()

        # 旋转按钮
        rotate_btn = QAction("旋转", self)
        rotate_btn.setIcon(self.style().standardIcon(self.style().SP_BrowserReload))
        rotate_btn.triggered.connect(self.rotate_image)
        toolbar.addAction(rotate_btn)

        # 缩放按钮
        resize_btn = QAction("缩放", self)
        resize_btn.setIcon(self.style().standardIcon(self.style().SP_FileDialogDetailedView))
        resize_btn.triggered.connect(self.resize_image)
        toolbar.addAction(resize_btn)

        # 裁剪按钮
        crop_btn = QAction("裁剪", self)
        crop_btn.setIcon(self.style().standardIcon(self.style().SP_LineEditClearButton))
        crop_btn.triggered.connect(self.enable_crop_mode)
        toolbar.addAction(crop_btn)

        toolbar.addSeparator()

        # 亮度/对比度按钮
        brightness_btn = QAction("亮度/对比度", self)
        brightness_btn.triggered.connect(self.show_brightness_controls)
        toolbar.addAction(brightness_btn)

        # 饱和度按钮
        saturation_btn = QAction("饱和度", self)
        saturation_btn.triggered.connect(self.show_saturation_controls)
        toolbar.addAction(saturation_btn)

        # 平滑处理按钮
        smooth_btn = QAction("平滑处理", self)
        smooth_btn.triggered.connect(self.show_smooth_controls)
        toolbar.addAction(smooth_btn)

        # 形状识别按钮
        shapes_btn = QAction("形状识别", self)
        shapes_btn.triggered.connect(self.detect_shapes)
        toolbar.addAction(shapes_btn)

        # 图像拼接按钮
        stitch_btn = QAction("图像拼接", self)
        stitch_btn.triggered.connect(self.stitch_images)
        toolbar.addAction(stitch_btn)

    def create_control_panel(self):
        """创建控制面板"""
        control_widget = QWidget()
        control_layout = QVBoxLayout(control_widget)

        # 创建各个功能组
        self.brightness_group = self.create_brightness_group()
        self.saturation_group = self.create_saturation_group()
        self.smooth_group = self.create_smooth_group()

        # 添加到控制面板布局
        control_layout.addWidget(self.brightness_group)
        control_layout.addWidget(self.saturation_group)
        control_layout.addWidget(self.smooth_group)
        control_layout.addStretch()

        return control_widget

    def create_brightness_group(self):
        """创建亮度/对比度控制组"""
        group = QGroupBox("亮度/对比度")
        layout = QGridLayout(group)

        # 亮度滑块
        brightness_label = QLabel("亮度:")
        self.brightness_slider = QSlider(Qt.Horizontal)
        self.brightness_slider.setRange(-100, 100)
        self.brightness_slider.setValue(0)
        self.brightness_slider.valueChanged.connect(self.update_brightness_contrast)

        self.brightness_value = QLabel("0")
        self.brightness_slider.valueChanged.connect(lambda value: self.brightness_value.setText(str(value)))

        # 对比度滑块
        contrast_label = QLabel("对比度:")
        self.contrast_slider = QSlider(Qt.Horizontal)
        self.contrast_slider.setRange(-100, 100)
        self.contrast_slider.setValue(0)
        self.contrast_slider.valueChanged.connect(self.update_brightness_contrast)

        self.contrast_value = QLabel("0")
        self.contrast_slider.valueChanged.connect(lambda value: self.contrast_value.setText(str(value)))

        # 应用按钮
        apply_btn = QPushButton("应用")
        apply_btn.clicked.connect(self.apply_brightness_contrast)

        # 添加到布局
        layout.addWidget(brightness_label, 0, 0)
        layout.addWidget(self.brightness_slider, 0, 1)
        layout.addWidget(self.brightness_value, 0, 2)

        layout.addWidget(contrast_label, 1, 0)
        layout.addWidget(self.contrast_slider, 1, 1)
        layout.addWidget(self.contrast_value, 1, 2)

        layout.addWidget(apply_btn, 2, 0, 1, 3)

        return group

    def create_saturation_group(self):
        """创建饱和度控制组"""
        group = QGroupBox("饱和度")
        layout = QGridLayout(group)

        # 饱和度滑块
        saturation_label = QLabel("饱和度:")
        self.saturation_slider = QSlider(Qt.Horizontal)
        self.saturation_slider.setRange(-100, 100)
        self.saturation_slider.setValue(0)
        self.saturation_slider.valueChanged.connect(self.update_saturation)

        self.saturation_value = QLabel("0")
        self.saturation_slider.valueChanged.connect(lambda value: self.saturation_value.setText(str(value)))

        # 应用按钮
        apply_btn = QPushButton("应用")
        apply_btn.clicked.connect(self.apply_saturation)

        # 添加到布局
        layout.addWidget(saturation_label, 0, 0)
        layout.addWidget(self.saturation_slider, 0, 1)
        layout.addWidget(self.saturation_value, 0, 2)

        layout.addWidget(apply_btn, 1, 0, 1, 3)

        return group

    def create_smooth_group(self):
        """创建平滑处理控制组"""
        group = QGroupBox("平滑处理")
        layout = QGridLayout(group)

        # 方法选择
        method_label = QLabel("方法:")
        self.smooth_method = QComboBox()
        self.smooth_method.addItems(["均值滤波", "高斯滤波", "中值滤波", "双边滤波"])

        # 内核大小
        kernel_label = QLabel("内核大小:")
        self.kernel_size = QSpinBox()
        self.kernel_size.setRange(1, 31)
        self.kernel_size.setValue(3)
        self.kernel_size.setSingleStep(2)  # 确保为奇数

        # 应用按钮
        apply_btn = QPushButton("应用")
        apply_btn.clicked.connect(self.apply_smooth)

        # 添加到布局
        layout.addWidget(method_label, 0, 0)
        layout.addWidget(self.smooth_method, 0, 1, 1, 2)

        layout.addWidget(kernel_label, 1, 0)
        layout.addWidget(self.kernel_size, 1, 1, 1, 2)

        layout.addWidget(apply_btn, 2, 0, 1, 3)

        return group

    def create_display_area(self):
        """创建显示区域"""
        display_widget = QTabWidget()

        # 原始图像标签
        self.original_label = QLabel("未加载图像")
        self.original_label.setAlignment(Qt.AlignCenter)

        # 处理后图像标签（可点击，支持裁剪）
        self.processed_label = ClickableImageLabel()
        self.processed_label.setAlignment(Qt.AlignCenter)
        self.processed_label.region_selected.connect(self.crop_image)

        # 添加标签页
        display_widget.addTab(self.original_label, "原始图像")
        display_widget.addTab(self.processed_label, "处理后图像")

        return display_widget

    def open_image(self):
        """打开图像文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "打开图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp *.gif);;所有文件 (*)"
        )

        if file_path:
            if self.processor.load_image(file_path):
                self.update_image_display()
                self.statusBar().showMessage(f"已加载图像: {os.path.basename(file_path)}")
            else:
                QMessageBox.critical(self, "错误", "无法加载图像，请检查文件格式或权限。")

    def save_image(self):
        """保存当前图像"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "警告", "没有可保存的图像")
            return

        if not self.processor.modified:
            QMessageBox.information(self, "提示", "图像未修改，无需保存")
            return

        if self.processor.file_path:
            try:
                cv2.imwrite(self.processor.file_path, self.processor.current_image)
                self.processor.modified = False
                self.statusBar().showMessage(f"已保存图像: {os.path.basename(self.processor.file_path)}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"保存图像失败: {str(e)}")
        else:
            self.save_image_as()

    def save_image_as(self):
        """另存为图像"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "警告", "没有可保存的图像")
            return

        file_path, _ = QFileDialog.getSaveFileName(
            self, "保存图像", "", "PNG 文件 (*.png);;JPEG 文件 (*.jpg);;BMP 文件 (*.bmp);;所有文件 (*)"
        )

        if file_path:
            try:
                # 确保文件扩展名正确
                if not any(file_path.lower().endswith(ext) for ext in ['.png', '.jpg', '.jpeg', '.bmp']):
                    if _.startswith("PNG"):
                        file_path += ".png"
                    elif _.startswith("JPEG"):
                        file_path += ".jpg"
                    elif _.startswith("BMP"):
                        file_path += ".bmp"

                cv2.imwrite(file_path, self.processor.current_image)
                self.processor.file_path = file_path
                self.processor.modified = False
                self.statusBar().showMessage(f"已保存图像: {os.path.basename(file_path)}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"保存图像失败: {str(e)}")

    def undo(self):
        """撤销操作"""
        if self.processor.original_image is not None:
            self.processor.current_image = self.processor.original_image.copy()
            self.processor.modified = False
            self.update_image_display()
            self.statusBar().showMessage("已撤销所有操作")

    def rotate_image(self):
        """旋转图像"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "警告", "没有加载图像")
            return

        angle, ok = QInputDialog.getDouble(
            self, "旋转图像", "输入旋转角度:", 0, -360, 360, 1
        )

        if ok:
            if self.processor.rotate(angle):
                self.update_image_display()
                self.statusBar().showMessage(f"图像已旋转 {angle} 度")

    def resize_image(self):
        """缩放图像"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "警告", "没有加载图像")
            return

        scale, ok = QInputDialog.getDouble(
            self, "缩放图像", "输入缩放百分比:", 100, 1, 1000, 1
        )

        if ok:
            if self.processor.resize(scale):
                self.update_image_display()
                self.statusBar().showMessage(f"图像已缩放至 {scale}%")

    def enable_crop_mode(self):
        """启用裁剪模式"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "警告", "没有加载图像")
            return

        self.statusBar().showMessage("请在图像上拖动选择裁剪区域")
        # 裁剪模式通过 ClickableImageLabel 的鼠标事件处理

    def crop_image(self, x, y, w, h):
        """裁剪图像"""
        if self.processor.current_image is None:
            return

        if self.processor.crop(x, y, w, h):
            self.update_image_display()
            self.statusBar().showMessage(f"图像已裁剪: {w}x{h} 像素")

    def update_brightness_contrast(self):
        """更新亮度/对比度预览"""
        brightness = self.brightness_slider.value()
        contrast = self.contrast_slider.value()

        # 保存当前图像状态
        if not hasattr(self, 'temp_image') or self.temp_image is None:
            self.temp_image = self.processor.current_image.copy()

        # 创建临时处理器进行预览
        temp_processor = ImageProcessor()
        temp_processor.current_image = self.temp_image.copy()
        temp_processor.adjust_brightness_contrast(brightness, contrast)

        # 更新显示
        qimage = temp_processor.to_qimage(temp_processor.current_image)
        self.processed_label.setPixmap(QPixmap.fromImage(qimage))

    def apply_brightness_contrast(self):
        """应用亮度/对比度调整"""
        brightness = self.brightness_slider.value()
        contrast = self.contrast_slider.value()

        if self.processor.adjust_brightness_contrast(brightness, contrast):
            self.temp_image = None  # 清除临时图像
            self.update_image_display()
            self.statusBar().showMessage(f"亮度/对比度已调整")

    def update_saturation(self):
        """更新饱和度预览"""
        saturation = self.saturation_slider.value()

        # 保存当前图像状态
        if not hasattr(self, 'temp_image') or self.temp_image is None:
            self.temp_image = self.processor.current_image.copy()

        # 创建临时处理器进行预览
        temp_processor = ImageProcessor()
        temp_processor.current_image = self.temp_image.copy()
        temp_processor.adjust_saturation(saturation)

        # 更新显示
        qimage = temp_processor.to_qimage(temp_processor.current_image)
        self.processed_label.setPixmap(QPixmap.fromImage(qimage))

    def apply_saturation(self):
        """应用饱和度调整"""
        saturation = self.saturation_slider.value()

        if self.processor.adjust_saturation(saturation):
            self.temp_image = None  # 清除临时图像
            self.update_image_display()
            self.statusBar().showMessage(f"饱和度已调整")

    def apply_smooth(self):
        """应用平滑处理"""
        method = self.smooth_method.currentText()
        kernel_size = self.kernel_size.value()

        if self.processor.smooth(method, kernel_size):
            self.update_image_display()
            self.statusBar().showMessage(f"已应用 {method} (内核大小: {kernel_size})")

    def detect_shapes(self):
        """检测图像中的形状"""
        if self.processor.current_image is None:
            QMessageBox.warning(self, "警告", "没有加载图像")
            return

        if self.processor.detect_shapes():
            self.update_image_display()
            self.statusBar().showMessage("形状识别完成")

    def stitch_images(self):
        """拼接图像"""
        if not self.images_to_stitch:
            # 如果没有已选择的图像，先添加当前图像
            if self.processor.current_image is not None:
                self.images_to_stitch.append(self.processor.current_image.copy())
                self.statusBar().showMessage(f"已添加当前图像到拼接队列 (共 {len(self.images_to_stitch)} 张)")
            else:
                QMessageBox.warning(self, "警告", "没有加载图像")
                return

        # 选择更多图像
        file_paths, _ = QFileDialog.getOpenFileNames(
            self, "选择要拼接的图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp);;所有文件 (*)"
        )

        if file_paths:
            for file_path in file_paths:
                img = cv2.imread(file_path)
                if img is not None:
                    self.images_to_stitch.append(img)

            self.statusBar().showMessage(f"已添加 {len(file_paths)} 张图像到拼接队列 (共 {len(self.images_to_stitch)} 张)")

            # 如果有足够的图像，询问是否执行拼接
            if len(self.images_to_stitch) >= 2:
                reply = QMessageBox.question(
                    self, "图像拼接",
                    f"共有 {len(self.images_to_stitch)} 张图像，是否执行拼接？",
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes
                )

                if reply == QMessageBox.Yes:
                    if self.processor.stitch_images(self.images_to_stitch):
                        self.update_image_display()
                        self.statusBar().showMessage("图像拼接完成")
                        self.images_to_stitch = []  # 清空队列
                    else:
                        QMessageBox.critical(self, "错误", "图像拼接失败，请确保图像有足够的重叠区域")
            else:
                QMessageBox.information(self, "提示", "至少需要两张图像才能进行拼接")

    def update_image_display(self):
        """更新图像显示"""
        if self.processor.original_image is not None:
            qimage_original = self.processor.to_qimage(self.processor.original_image)
            self.original_label.setPixmap(QPixmap.fromImage(qimage_original))

        if self.processor.current_image is not None:
            qimage_processed = self.processor.to_qimage(self.processor.current_image)
            self.processed_label.setPixmap(QPixmap.fromImage(qimage_processed))

    def show_brightness_controls(self):
        """显示亮度/对比度控制面板"""
        self.brightness_group.show()

    def show_saturation_controls(self):
        """显示饱和度控制面板"""
        self.saturation_group.show()

    def show_smooth_controls(self):
        """显示平滑处理控制面板"""
        self.smooth_group.show()

    def show_about(self):
        """显示关于对话框"""
        QMessageBox.about(
            self, "关于",
            "计算机视觉开发平台\n\n"
            "版本: 1.0\n"
            "功能: 图像加载、编辑、色彩调整、平滑处理、形状识别和图像拼接\n"
            "技术: Python + PyQt5 + OpenCV"
        )


if __name__ == "__main__":
    # 确保中文显示正常
    os.environ["QT_FONT_DPI"] = "96"

    app = QApplication(sys.argv)
    app.setStyle('Fusion')  # 使用Fusion风格，跨平台一致性更好

    # 设置全局字体
    font = QFont("SimHei")  # 使用黑体
    app.setFont(font)

    window = MainWindow()
    sys.exit(app.exec_())

